package novel.impl.downLoad;

import novel.callable.DownloadCallable;
import novel.config.Configuration;
import novel.entity.Chapter;
import novel.enums.NovelSpiderEnum;
import novel.intrerfaces.INovelDownLoad;
import novel.intrerfaces.IchapterSpider;
import novel.factory.ChapterSpiderFactory;
import novel.util.NovelSpiderUtil;

import java.io.File;
import java.util.*;
import java.util.concurrent.*;

/**
 * @Author:xdw
 * @Date:Created in 上午 9:552019/3/12 0012
 * @described: 实现多线程下载任意网站的小说
 * 拿到该网站的某本小说的所有章节：章节列表
 * 通过计算，将这些章节分配给指定数量的线程，让他们去解析，然后保存到文本文件中
 * 通知主线程，将这些零散的小文件合并成一个大文件。最后将那些分片的小文件删除掉。
 * @Modified By:
 */
public class NovelDownLoad implements INovelDownLoad {
    @Override
    public String downLoad(String url, Configuration config) {
        //通过工程类获取章节接口
        IchapterSpider spider = ChapterSpiderFactory.getChapterSpider(url);
        List<Chapter> chapters = spider.getsChapter(url);
        //某个线程下载完毕之后，你得告诉主线程：我下载好了
        //所有的线程都下载好了，合并！！！
        int size = config.getSize();
        //最大线程数，向上取整
        int maxThreadSize = (int) Math.ceil(chapters.size() * 1.0 / size);

        //保存每个线程保存的章节列表
        Map<String, List<Chapter>> downloadTaskAlloc = new HashMap<>();

        for (int i = 0; i < maxThreadSize; i++) {
            //假设chapters.sizes=2050
            //maxThreadSize 21个线程
            //第一个线程 i=0 0-99, chapters.subList(0,99); 下载第一章到99章
            //第二个线程 i=1 100-200 ,chapterts.subList(100,200) 下载第100章到199章
            //...
            //i=20 2000-2050 到最后时 开始还是2000 结尾应该时总章节
            int fromIndex = i * config.getSize();

            int toIndex = i == maxThreadSize - 1 ? chapters.size() : i * config.getSize() + config.getSize();

            downloadTaskAlloc.put(fromIndex + "-" + toIndex, chapters.subList(fromIndex, toIndex));
        }
        //通过线程下载
        //定义线程池  创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(maxThreadSize);

        //获取需要遍历的key
        Set<String> keySet = downloadTaskAlloc.keySet();
        //线程返回的值
        List<Future<String>> tasks = new ArrayList<>();
        //指定下载地址，根据url来判断,创建缺失的路径
        String savePath = config.getPath() + "/"
                + NovelSpiderEnum.getEnumByUrl(url).getUrl();
        new File(savePath).mkdirs();
        for (String key : keySet) {
            tasks.add(executorService.submit(
                    new DownloadCallable(
                            savePath + "/" + key + ".txt",
                            downloadTaskAlloc.get(key), config.getTryTimes())));
        }
        //当所有线程结束时，关闭线程池
        executorService.shutdown();
        for (Future<String> future : tasks) {
            try {
                System.out.println(future.get() + ",下载完成！");
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        NovelSpiderUtil.multiFileMerge(savePath, null, false);
        return savePath + "/merge.txt";
    }
}
